use std::{
    cmp::Ordering,
    collections::HashMap,
    fs::File,
    io::{self, BufRead, BufReader, Read as _, Seek as _, SeekFrom},
    path::Path,
};

// 声韵母的映射规则
#[derive(Debug, Clone)]
struct PinyinRules {
    initials: HashMap<&'static str, &'static str>, // 声母映射
    finals: HashMap<&'static str, &'static str>,   // 韵母映射
}

impl PinyinRules {
    fn new() -> Self {
        let initials = HashMap::from([
            // 不送气音
            ("b", "p"),
            ("d", "t"),
            ("g", "k"),
            ("j", "ch"),
            ("zh", "ch"),
            ("z", "ts"),
            // 送气音
            ("p", "p'"),
            ("t", "t'"),
            ("k", "k'"),
            ("q", "ch'"),
            ("ch", "ch'"),
            ("c", "ts'"),
            // 其他
            ("m", "m"),
            ("n", "n"),
            ("l", "l"),
            ("h", "h"),
            ("x", "hs"),
            ("sh", "sh"),
            ("r", "j"),
            ("f", "f"),
            ("s", "s"),
            ("w", "w"),
            ("y", "y"),
        ]);

        let finals = HashMap::from([
            // 单元音
            ("a", "a"),
            ("o", "o"),
            ("e", "e"),
            ("i", "i"),
            ("u", "u"),
            ("ü", "ü"),
            // 双元音和三元音
            ("ai", "ai"),
            ("ei", "ei"),
            ("ao", "ao"),
            ("ou", "ou"),
            ("iao", "iao"),
            ("iu", "iu"),
            ("ui", "ui"),
            ("ie", "ieh"),
            ("üe", "üeh"),
            // 鼻音韵母
            ("an", "an"),
            ("en", "en"),
            ("in", "in"),
            ("un", "un"),
            ("ün", "ün"),
            ("ang", "ang"),
            ("eng", "eng"),
            ("ing", "ing"),
            ("ong", "ung"),
            // 复合韵母
            ("ua", "ua"),
            ("uo", "o"), // 特别处理
            ("uai", "uai"),
            ("uan", "uan"),
            ("uang", "uang"),
            ("iang", "iang"),
            ("ian", "ien"),
            ("iong", "iung"),
            // 特殊韵母
            ("er", "erh"),
        ]);

        PinyinRules { initials, finals }
    }
}

#[derive(Debug)]
pub struct WadeGilesConverter {
    rules: PinyinRules,
}

impl WadeGilesConverter {
    pub fn new() -> Self {
        WadeGilesConverter {
            rules: PinyinRules::new(),
        }
    }

    pub fn convert(&self, pinyin: &str) -> String {
        let pinyin = pinyin.trim().to_lowercase();

        if pinyin.is_empty() {
            return String::new();
        }

        // 特殊处理整体音节
        if let Some(special) = self.handle_special_cases(&pinyin) {
            return special;
        }

        // 分离声母和韵母
        let (initial, final_part) = self.analyze_pinyin(&pinyin);

        // 转换声母
        let wade_initial = self.convert_initial(&initial);

        // 转换韵母
        let wade_final = self.convert_final(&final_part, &initial);

        format!("{}{}", wade_initial, wade_final)
    }

    fn analyze_pinyin(&self, pinyin: &str) -> (String, String) {
        // 所有可能的声母，从长到短排序，以匹配最长的声母
        let possible_initials = vec![
            "zh", "ch", "sh", "z", "c", "s", "b", "p", "m", "f", "d", "t", "n", "l", "g", "k", "h",
            "j", "q", "x", "r", "w", "y",
        ];

        for initial in possible_initials {
            if pinyin.strip_prefix(initial).is_some() {
                let final_part = pinyin[initial.len()..].to_string();
                return (initial.to_string(), final_part);
            }
        }
        (String::new(), pinyin.to_string())
    }

    fn convert_initial(&self, initial: &str) -> String {
        if initial.is_empty() {
            String::new()
        } else {
            self.rules
                .initials
                .get(initial)
                .copied()
                .unwrap_or(initial)
                .to_string()
        }
    }

    fn convert_final(&self, final_part: &str, initial: &str) -> String {
        // 特殊处理 "iu", "ui", "un" 等需要添加介音的情况
        let final_with_medial = if ["j", "q", "x"].contains(&initial) && final_part.starts_with('u')
        {
            match final_part {
                "u" => "ü",
                "ue" => "üe",
                "uan" => "üan",
                "un" => "ün",
                _ => final_part,
            }
            .to_string()
        } else if initial.is_empty() && final_part.starts_with('u') {
            // 零声母
            final_part.to_string()
        } else {
            final_part.to_string()
        };

        // 处理韵母映射
        let mut wade_final = self
            .rules
            .finals
            .get(final_with_medial.as_str())
            .copied()
            .unwrap_or(final_with_medial.as_str())
            .to_string();

        if final_part == "uo" && ["g", "k", "h"].contains(&initial) {
            wade_final = "uo".to_string();
        }
        if final_part == "e" && ["g", "k"].contains(&initial) {
            wade_final = "o".to_string();
        }

        // 零声母时
        if initial.is_empty() {
            if final_part.starts_with('i') {
                wade_final = format!("y{}", wade_final);
            } else if final_part.starts_with('u') {
                wade_final = format!("w{}", wade_final);
            } else if final_part.starts_with('ü') {
                wade_final = format!("y{}", wade_final);
            }
        }

        wade_final
    }

    fn handle_special_cases(&self, pinyin: &str) -> Option<String> {
        // 特殊音节的映射
        let special_cases = HashMap::from([
            ("zhi", "chih"),
            ("chi", "ch'ih"),
            ("shi", "shih"),
            ("ri", "jih"),
            ("zi", "tzŭ"),
            ("ci", "tz'ŭ"),
            ("si", "ssu"),
            ("yi", "i"),
            ("ya", "ya"),
            ("ye", "yeh"),
            ("yao", "yao"),
            ("you", "yu"),
            ("yan", "yen"),
            ("yin", "yin"),
            ("yang", "yang"),
            ("ying", "ying"),
            ("yong", "yung"),
            ("wu", "wu"),
            ("wa", "wa"),
            ("wo", "wo"),
            ("wai", "wai"),
            ("wei", "wei"),
            ("wan", "wan"),
            ("wen", "wen"),
            ("wang", "wang"),
            ("weng", "weng"),
            ("yu", "yü"),
            ("yue", "yüeh"),
            ("yuan", "yüan"),
            ("yun", "yün"),
        ]);

        special_cases.get(pinyin).map(|s| s.to_string())
    }
}

struct PinyinConverter {
    dict: File,
}

impl PinyinConverter {
    pub fn new(dict_path: &str) -> Self {
        let dict = File::open(Path::new(dict_path)).unwrap();
        PinyinConverter { dict }
    }
    fn parse_line(line: &str) -> Option<(u32, String)> {
        if line.is_empty() {
            return None;
        }
        let parts: Vec<&str> = line.split(':').collect();
        if parts.len() < 2 {
            return None;
        }
        let code_str = parts[0].trim();
        let pinyin_part = parts[1].trim();
        let pinyin_parts: Vec<&str> = pinyin_part.split('#').collect();
        let pinyin = pinyin_parts[0].trim();

        // 去掉'U+'，解析为u32
        let code = u32::from_str_radix(code_str.trim_start_matches("U+"), 16).ok()?;
        Some((code, pinyin.to_string()))
    }
    pub fn binary_search_pinyin(&self, target_code: u32) -> io::Result<Option<String>> {
        let file_size = self.dict.metadata()?.len();
        let mut low = 0;
        let mut high = file_size;

        let mut reader = BufReader::new(&self.dict);

        while low <= high {
            // println!("low: {}, high: {}", low, high);
            let mut mid = (low + high) / 2;
            let mut buffer = [0; 1];
            let mut i = 5;
            'outer: loop {
                if mid <= 5 {
                    mid = 0;
                    reader.seek(SeekFrom::Start(0))?;
                    break;
                }
                mid -= 5;
                reader.seek(SeekFrom::Start(mid))?;
                while i > 0 {
                    while reader.read(&mut buffer)? != 0 {
                        if buffer[0] == b'\n' {
                            break 'outer;
                        }
                    }
                    i -= 1;
                }
                i = 5;
            }
            let mut line = String::new();
            reader.read_line(&mut line)?;

            if let Some((code, pinyin)) = Self::parse_line(&line) {
                // println!("code:{},taget_code:{}", code, target_code);
                match code.cmp(&target_code) {
                    Ordering::Equal => {
                        let pinyin_no_tone = Self::remove_tone_marks(&pinyin);
                        return Ok(Some(pinyin_no_tone));
                    }
                    Ordering::Less => {
                        low = reader.stream_position()?; // 更新为当前指针位置
                    }
                    Ordering::Greater => {
                        if mid == 0 {
                            break;
                        }
                        high = mid;
                    }
                }
            } else {
                low = reader.stream_position()?;
            }
        }

        Ok(None)
    }
    fn remove_tone_marks(pinyin: &str) -> String {
        pinyin
            .chars()
            .map(|c| match c {
                'ā' | 'á' | 'ǎ' | 'à' | 'a' => 'a',
                'ē' | 'é' | 'ě' | 'è' | 'e' => 'e',
                'ī' | 'í' | 'ǐ' | 'ì' | 'i' => 'i',
                'ō' | 'ó' | 'ǒ' | 'ò' | 'o' => 'o',
                'ū' | 'ú' | 'ǔ' | 'ù' | 'u' => 'u',
                'ǖ' | 'ǘ' | 'ǚ' | 'ǜ' | 'ü' => 'u',
                'ń' | 'ň' | 'ǹ' | 'n' => 'n',
                'ḿ' | 'm' => 'm',
                _ => c,
            })
            .collect()
    }
    pub fn convert(&self, words: &str) -> io::Result<Vec<String>> {
        let mut result = Vec::new();
        for u in words.chars() {
            let pinyin = self.binary_search_pinyin(u as u32)?;
            result.push(pinyin.unwrap_or_default());
        }
        Ok(result)
    }
}

pub fn converter(input: &str) -> String {
    let hz_converter = PinyinConverter::new("data/pinyin.txt");
    let py_converter = WadeGilesConverter::new();
    let pinyin = hz_converter.convert(input).unwrap();
    let converted_pinyin: Vec<String> = pinyin
        .into_iter()
        .map(|py| py_converter.convert(&py))
        .collect();
    let mut result = converted_pinyin.join(" ");
    if !result.is_empty() {
        result.replace_range(
            ..1,
            &result.chars().next().unwrap().to_uppercase().to_string(),
        );
    }
    result
}

// 单元测试
// #[cfg(test)]
// mod tests {
//     use super::*;

//     #[test]
//     fn test_basic_conversion() {
//         let converter = WadeGilesConverter::new();
//         assert_eq!(converter.convert("zhong"), "chung");
//         assert_eq!(converter.convert("guo"), "kuo");
//         assert_eq!(converter.convert("ren"), "jen");
//         assert_eq!(converter.convert("shi"), "shih");
//         assert_eq!(converter.convert("ri"), "jih");
//         assert_eq!(converter.convert("zhi"), "chih");
//         assert_eq!(converter.convert("chi"), "ch'ih");
//         assert_eq!(converter.convert("ci"), "tz'ŭ");
//         assert_eq!(converter.convert("yao"), "yao");
//         assert_eq!(converter.convert("zui"), "tsui");
//         assert_eq!(converter.convert("bei"), "pei");
//         assert_eq!(converter.convert("jing"), "ching");
//         assert_eq!(converter.convert("zhu"), "chu");
//         assert_eq!(converter.convert("ge"), "ko");
//         assert_eq!(converter.convert("liang"), "liang");
//         assert_eq!(converter.convert("shang"), "shang");
//         assert_eq!(converter.convert("hai"), "hai");
//         assert_eq!(converter.convert("mei"), "mei");
//         assert_eq!(converter.convert("guo"), "kuo");
//         assert_eq!(converter.convert("cao"), "ts'ao");
//     }

//     #[test]
//     fn test_special_cases() {
//         let converter = WadeGilesConverter::new();
//         assert_eq!(converter.convert("yi"), "i");
//         assert_eq!(converter.convert("wu"), "wu");
//         assert_eq!(converter.convert("yu"), "yü");
//         assert_eq!(converter.convert("yue"), "yüeh");
//         assert_eq!(converter.convert("yuan"), "yüan");
//         assert_eq!(converter.convert("yun"), "yün");
//         assert_eq!(converter.convert("ju"), "chü");
//         assert_eq!(converter.convert("qu"), "ch'ü");
//         assert_eq!(converter.convert("xu"), "hsü");
//     }

//     #[test]
//     fn test_zero_initial() {
//         let converter = WadeGilesConverter::new();
//         assert_eq!(converter.convert("ai"), "ai");
//         assert_eq!(converter.convert("ou"), "ou");
//         assert_eq!(converter.convert("an"), "an");
//         assert_eq!(converter.convert("en"), "en");
//     }

//     #[test]
//     fn test_empty_input() {
//         let converter = WadeGilesConverter::new();
//         assert_eq!(converter.convert(""), "");
//     }

//     #[test]
//     fn test_hanzi_to_pinyin() {
//         let mut converter = PinyinConverter::new("data/pinyin.txt");
//         // let pinyin = converter.binary_search_pinyin(0x4E00).unwrap();
//         // assert_eq!(pinyin.unwrap(), "yi");
//         // assert_eq!(
//         //     converter.binary_search_pinyin(0x3447).unwrap().unwrap(),
//         //     "zhou"
//         // );
//         assert_eq!(
//             converter.binary_search_pinyin(0x4F60).unwrap().unwrap(),
//             "ni"
//         );
//     }

// #[test]
// fn test_convert_hanzi_to_pinyin() {
// let converter = PinyinConverter::new("data/pinyin.txt");
// println!("{}", converter.convert("湾").unwrap()[0]);
// assert_eq!(converter.convert("你好").unwrap(), vec!["ni", "hao"]);
// assert_eq!(converter.convert("我爱你").unwrap(), vec!["wo", "ai", "ni"]);
// assert_eq!(
//     converter.convert("中华人民共和国").unwrap(),
//     vec!["zhong", "hua", "ren", "min", "gong", "he", "guo"]
// );
// assert_eq!(converter.convert("上海").unwrap(), vec!["shang", "hai"])
//     }
// }
